--[[
Copyright (C) 2008-2009 Adirelle

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--]]

if not Yatba then return end
local Yatba = Yatba

local pairs, type, unpack = pairs, type, unpack
local L, new, del = Yatba.L, Yatba.new, Yatba.del

local LibBars = LibStub("LibBars-1.0")

--------------------------------------------------------------------------------
-- Database upvalue
--------------------------------------------------------------------------------

local db
Yatba.RegisterSignal('OptionsDB', 'DatabaseSet', function() db = Yatba.db.profile end)

--------------------------------------------------------------------------------
-- Option Proxy Object
--------------------------------------------------------------------------------

local optionProxy = setmetatable({}, {
	__index = function(proxy, name)
		local attribute = Yatba[name]
		if type(attribute) == "function" then
			local methodProxy = function(proxy, info, ...) return attribute(Yatba, ...) end
			proxy[name] = methodProxy
			return methodProxy
		else
			return attribute
		end
	end
})

function optionProxy:GetOption(info)
	local optKey, optType = info[#info], info.option.type
	if optType == 'color' then
		return unpack(db.colors[optKey])
	else
		return db[optKey]
	end
end

function optionProxy:SetOption(info, ...)
	local optKey, optType = info[#info], info.option.type
	if optType == 'color' then
		local color = db.colors[optKey]
		if type(color) == 'table' then
			color[1], color[2], color[3], color[4] = ...
		else
			db.colors[optKey] = { ... }
		end
		Yatba:SendSignal('ColorChanged_'..optKey)
	else
	db[optKey] = ...
		Yatba:SendSignal('ConfigChanged_'..optKey)
	end
end

--------------------------------------------------------------------------------
-- Global options
--------------------------------------------------------------------------------

local globalGroupOptionList = {}
local globalAuraOptionList = {}

local auraDefaultFilteringValues = {
	mine   = L['Show only mine'],
	ignore = L['Hide all'],
	all    = L["Show all"],
}

Yatba.options = {
	handler = optionProxy,
	name = function() return ("Yatba |cffffffff%s|r"):format(Yatba.version) end,
	type = 'group',
	get = 'GetOption',
	set = 'SetOption',
	args = {
		enable = {
			order = 100,
			name = L['Enabled'],
			desc = L['Enable Yatba.'],
			type = 'toggle',
			get = 'IsEnabled',
			set = function(info, v)
				if v then
					Yatba:Enable()
				elseif not v then
					Yatba:Disable()
				end
			end,
		},
		lock = {
			order = 110,
			name = L['Locked'],
			type = 'toggle',
			tristate = true,
			get = 'IsLocked',
			set = 'SetLock',
		},
--[===[@debug@
		debug = LibStub('LibDebugLog-1.0'):GetAce3OptionTable(Yatba, 115),
--@end-debug@]===]
		layout = {
			order = 120,
			name = L['General layout'],
			type = 'group',
			args = {
				highlights = {
					order = 100,
					name = L['Highlights'],
					desc = L['Enable target and/or focus bar highlighting. You can configure the highlight color and opacity in the color section.'],
					type = 'group',
					inline = true,
					args = {
						highlightTarget = {
							order = 100,
							name = L['Target'],
							desc = L['Highlight all bar belonging to your current target.'],
							type = 'toggle',
						},
						highlightFocus = {
							order = 110,
							name = L['Focus'],
							desc = L['Highlight all bar belonging to your current focus.'],
							type = 'toggle',
						},
						highlightTexture = {
							order = 115,
							name = L['Texture'],
							desc = L['Set the highlight texture.'],
							type = 'select',
							values = {
								['Plain'] = L['Plain'],
								['Border'] = L['Border'],
								['ThinBorder'] = L['ThinBorder'],
							},
						},
					},
				},
				bars = {
					order = 110,
					name = L['Bars'],
					type = 'group',
					inline = true,
					args = {
						statusbar = {
							order = 120,
							name = L['Texture'],
							type = 'select',
							dialogControl = 'LSM30_Statusbar',
							values = AceGUIWidgetLSMlists.statusbar,
						},
						thickness = {
							order = 160,
							name = L['Thickness'],
							type = 'range',
							min = 15,
							max = 50,
							step = 1,
						},
						headerHealth = {
							order = 170,
							name = L['Display unit health in headers'],
							desc = L['When enabled, headers display the unit health, depleting or filling as the unit loses or gains life.'],
							type = 'toggle',
							width = 'double',
						},
						showRaidIcons = {
							order = 180,
							name = L['Display raid icons'],
							desc = L['Display raid icons in the bar labels. Enable the use of $i variable in text template.'],
							type = 'toggle',
							width = 'double',
						}
					},
				},
				font = {
					order = 120,
					name = L['Font'],
					type = 'group',
					inline = true,
					args = {
						font = {
							order = 130,
							name = L['Name'],
							type = 'select',
							dialogControl = 'LSM30_Font',
							values = AceGUIWidgetLSMlists.font,
						},
						fontSize = {
							order = 140,
							name = L['Size'],
							type = 'range',
							min = 6,
							max = 30,
							step = 1,
						},
						fontFlags = {
							order = 150,
							name = L['Decoration'],
							type = 'select',
							values = {
								[""] = L['None'],
								OUTLINE = L['Outline'],
								THICKOUTLINE = L['Thick outline'],
							},
						},
					},
				},
			},
		},
		colors = {
			name = L['Colors'],
			type = 'group',
			order = 130,
			args = {
				highlight = {
					order = 100,
					name = L['Highlight colors'],
					type = 'group',
					inline = true,
					args = {
						targetHighlight = {
							order = 100,
							name = L['Target'],
							type = 'color',
							hasAlpha = true,
							disabled = function() return not db.highlightTarget end,
						},
						focusHighlight = {
							order = 110,
							name = L['Focus'],
							type = 'color',
							hasAlpha = true,
							disabled = function() return not db.highlightFocus end,
						},
					},
				},
				headers = {
					order = 110,
					name = L['Unit headers'],
					type = 'group',
					inline = true,
					args = {
						healthGradient = {
							order = 100,
							name = L['Health-based color'],
							desc = L['When enabled, header bars uses a color gradient depending on unit health.'],
							type = 'toggle',
							disabled = function() return not db.headerHealth end,
							width = 'full',
						},
						header = {
							order = 120,
							name = L['Header'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return db.headerHealth and db.healthGradient end,
						},
						lowHealth = {
							order = 120,
							name = L['Low health'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.headerHealth or not db.healthGradient end,
						},
						highHealth = {
							order = 125,
							name = L['Full life'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.headerHealth or not db.healthGradient end,
						},
					}
				},
				timers = {
					order = 130,
					name = L['Timer bars'],
					type = 'group',
					inline = true,
					args = {
						debuffColorByType = {
							order = 100,
							name = L['Color debuffs by type'],
							type = 'toggle',
							width = 'full',
						},
						buff = {
							order = 110,
							name = L['Buffs'],
							type = 'color',
							hasAlpha = false,
						},
						debuff = {
							order = 120,
							name = L['Debuffs'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return db.debuffColorByType end,
						},
						debuffMagic = {
							order = 120,
							name = L['Magic debuffs'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.debuffColorByType end,
						},
						debuffCurse = {
							order = 120,
							name = L['Curses'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.debuffColorByType end,
						},
						debuffDisease = {
							order = 120,
							name = L['Diseases'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.debuffColorByType end,
						},
						debuffPoison = {
							order = 120,
							name = L['Poisons'],
							type = 'color',
							hasAlpha = false,
							hidden = function() return not db.debuffColorByType end,
						}
					},
				},
			},
		},
		groups = {
			name = L['Bar groups'],
			type = 'group',
			order = 140,
			plugins = {
				groupList = globalGroupOptionList,
			},
			args = {
				new = {
					name = L['New group'],
					desc = L['Create a new bar group.'],
					type = "input",
					get = false,
					set = 'InitializeGroup',
					pattern = '^%w+$',
					order = 0,
				},
				remove = {
					order = 1,
					name = L['Remove'],
					desc = L['Definitively delete a existing bar group.'],
					type = 'select',
					values = 'GetGroupList',
					confirm = function() return L["Definitively remove this group ?"] end,
					get = false,
					set = 'RemoveGroup',
					disabled = function() return not next(db.groups) end,
				},
				enable = {
					order = 2,
					name = L['Enabled groups'],
					desc = L['Uncheck a group to hide it. Its bar will be dispatched to other groups.'],
					type = 'multiselect',
					values = 'GetGroupList',
					get = 'IsGroupEnabled',
					set = 'SetGroupEnabled',
				},
			},
		},
		auras = {
			name = L['Advanced aura filtering'],
			type = 'group',
			order = 150,
			disabled = function(info) return not db.advancedFiltering and info[#info] ~= 'auras' end,
			args = {
				advancedFiltering = {
					name = L['Enable'],
					type = 'toggle',
					width = 'full',
					order = 100,
					disabled = false,
				},
				_help = {
					name = L['Select the default rule then create a list of auras that follow different rules.'],
					type = 'description',
					order = 101,
				},
				defaultRule = {
					name = L['Default rule'],
					order = 110,
					type = 'select',
					values = auraDefaultFilteringValues,
					disabled = function() return not db.advancedFiltering end,
				},
				add = {
					name = L['Add specific aura rule'],
					desc = L['Enter the name of an aura for which a specific rule should be applied.'],
					order = 120,

					type = 'input',
					get = false,
					set = 'AddAura',
					validate = 'ValidateAuraName',
				},
				remove = {
					name = L['Remove specific aura rule'],
					desc = L['Select an aura for which the specific rule should be removed.'],
					order = 130,
					type = 'select',
					get = false,
					set = 'RemoveAura',
					values = 'GetGlobalAuraList',
					confirm = function() return L['Remove this aura and all related filters ?'] end,
					disabled = function() return not db.advancedFiltering or not next(db.auraRules) end,
				},
				removeAll = {
					name = L['Remove all aura rules'],
					order = 140,
					type = 'execute',
					func = function()
						for id in pairs(db.auraRules) do
							Yatba:RemoveAura(id)
						end
						Yatba:SendSignal('AuraFilterUpdated')
					end,
					disabled = function() return not db.advancedFiltering or not next(db.auraRules) end,
				},
				auraRules = {
					name = L['Specific aura rules'],
					desc = L['Define the rule of each aura you added to the list.'],
					order = 150,
					type = 'group',
					inline = true,
					args = globalAuraOptionList,
					hidden = function() return not next(db.auraRules) end,
				},
			},
		},
	}
}

--------------------------------------------------------------------------------
-- Group options
--------------------------------------------------------------------------------

local groupOptions
local groupAuraOptionList = {}

groupOptions = {
	type = 'group',
	get = function(info, k)
		local optName, optDB = info[#info], info.handler.db
		if info.option.type == 'multiselect' then
			return optDB[optName] and optDB[optName][k]
		else
			return optDB[optName]
		end
	end,
	set = function(info, k, v)
		local optName, optDB = info[#info], info.handler.db
		if info.option.type == 'multiselect' then
			optDB[optName][k] = v
		else
			optDB[optName] = k
		end
	end,
	childGroups = 'tab',
	name = function(info)
		local name = info.handler.name
		if info.handler.db and not info.handler.db.enable then
			name = ("|cff777777%s|r"):format(name)
		end
		return name
	end,
	hidden = function(info)	return not info.handler.db.enable	end,
	args = {
		filtering = {
			order = 100,
			name = L['Filtering'],
			desc = L['Filter which timer bars should be shown is this group.'],
			type = 'group',
			set = function(info, k, v)
				groupOptions.set(info, k, v)
				Yatba:UpdateFilter(info.handler)
			end,
			args = {
				priority = {
					order = 100,
					name = L['Priority'],
					desc = L['The priority of this group. When a timer bar fits in several groups, it goes into the one with higher priority.'],
					type = 'range',
					min = 0,
					max = 100,
					step = 1,
				},
				auraTypes = {
					order = 110,
					name = L['Aura types'],
					desc = L['Select aura types to be displayed in this group.'],
					type = 'multiselect',
					values = {
						BUFF = L['Buffs'],
						DEBUFF = L['Debuffs'],
					}
				},
				units = {
					order = 120,
					name = L['Units'],
					desc = L['Select units to be displayed in this group. Notice that the entries do not overlap, e.g. your are not included in other party members.'],
					type = 'multiselect',
					values = {
						player   = L['Yourself'],
						pet      = L['Your pet'],
						party    = L['Other party members'],
						raid     = L['Other raid members'],
						friendly = L['Other friendly units'],
						hostile  = L['Hostile units'],
					},
				},
				auraRules = {
					order = 125,
					name = L['Advanced aura filtering'],
					desc = L['Select the rules to apply to each of these auras.'],
					type = 'group',
					inline = true,
					args = groupAuraOptionList,
					hidden = function() return not db.advancedFiltering or not Yatba:HasGroupAuraFilter() end
				},
				duration = {
					order = 130,
					name = L['Duration'],
					type = 'group',
					inline = true,
					args = {
						durationFilter = {
							order = 100,
							name = L['Enabled'],
							desc = L['Enable filtering based on aura total duration.'],
							type = 'toggle',
						},
						minDuration = {
							order = 110,
							name = L['Minimum duration'],
							desc = L['Auras with duration below this value are ignored.'],
							type = 'range',
							min = 0,
							max = 60*60,
							step = 1,
							bigStep = 5,
							disabled = function(info) return not info.handler.db.durationFilter end,
							validate = function(info, v)
								if info.handler.db.maxDuration > 0 and v >= info.handler.db.maxDuration then
									return L["Minimum duration must be lesser than maximum duration."]
								else
									return true
								end
							end
						},
						maxDuration = {
							order = 110,
							name = L['Maximum duration'],
							desc = L['Auras with duration above this value are ignored. Sets to 0 to disable this filter.'],
							type = 'range',
							min = 0,
							max = 60*60,
							step = 1,
							bigStep = 5,
							disabled = function(info) return not info.handler.db.durationFilter end,
							validate = function(info, v)
								if v > 0 and v <= info.handler.db.minDuration then
									return L["Maximum duration must be greater than minimum duration."]
								else
									return true
								end
							end
						},
					},
				},
			},
		},
		layout = {
			order = 120,
			name = L['Layout'],
			type = 'group',
			args = {
				geometry = {
					order = 100,
					name = L['Geometry'],
					type = 'group',
					inline = true,
					args = {
						left = {
							order = 90,
							name = L['Left'],
							type = 'range',
							step = 1,
							bigStep = 10,
							min = 0,
							max = UIParent:GetWidth(),
							get = function(info) return info.handler:GetLeft() end,
							set = function(info, v)
								info.handler:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", v, info.handler.db.y)
								info.handler.db.x = info.handler:GetLeft()
							end
						},
						top = {
							order = 95,
							name = L['Top'],
							type = 'range',
							step = 1,
							bigStep = 10,
							min = 0,
							max = UIParent:GetHeight(),
							get = function(info) return UIParent:GetHeight() - info.handler:GetTop() end,
							set = function(info, v)
								v = UIParent:GetHeight() - v
								info.handler:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", info.handler.db.x, v)
								info.handler.db.y = info.handler:GetTop()
							end
						},
						length = {
							order = 100,
							name = L['Length'],
							type = 'range',
							min = 15,
							max = 300,
							step = 1,
							bigStep = 5,
							get = function(info) return info.handler.db.length end,
							set = function(info, v)
								info.handler:SetLength(v)
								info.handler.db.length = v
							end,
						},
						scale = {
							order = 110,
							name = L['Scale'],
							type = 'range',
							min = 0.05,
							max = 3.0,
							isPercent = true,
							step = 0.01,
							bigStep = 0.05,
							get = function(info) return info.handler.db.scale end,
							set = function(info, v)
								info.handler:SetScale(v)
								info.handler.db.scale = v
							end,
						},
						orientation = {
							order = 150,
							name = L['Orientation'],
							type = 'select',
							values = {
								[LibBars.LEFT_TO_RIGHT] = L["Left to right"],
								[LibBars.BOTTOM_TO_TOP] = L["Bottom to top"],
								[LibBars.RIGHT_TO_LEFT] = L["Right to left"],
								[LibBars.TOP_TO_BOTTOM] = L["Top to bottom"],
							},
							get = "GetOrientation",
							set = function(info, v)
								info.handler:SetOrientation(v)
								info.handler.db.orientation = v
							end
						},
						spacing = {
							order = 155,
							name = L['Spacing'],
							type = 'range',
							min = 0,
							max = 100,
							step = 1,
							get = function(info) return info.handler:GetSpacing() end,
							set = function(info, v)
								info.handler:SetSpacing(v)
								info.handler.db.spacing = v
							end,
						},
						opacity = {
							order = 160,
							name = L['Opacity'],
							type = 'range',
							isPercent = true,
							min = 0.05,
							max = 1.0,
							step = 0.01,
							bigStep = 0.05,
							get = function(info) return info.handler:GetAlpha() end,
							set = function(info, v)
								info.handler:SetAlpha(v)
								info.handler.db.alpha = v
							end,
						},
						resetPosition = {
							order = 190,
							name = L['Reset position'],
							type = 'execute',
							func = function(info)
								info.handler:ClearAllPoints()
								info.handler:SetPoint('CENTER', UIParent, 'CENTER')
								info.handler.db.x = info.handler:GetLeft()
								info.handler.db.y = info.handler:GetTop()
							end,
						},
					},
				},
				growth = {
					order = 130,
					name = L['Growth'],
					type = 'group',
					inline = true,
					args = {
						growth = {
							order = 130,
							name = function(info)
								return info.handler:IsVertical() and L['Grows left'] or L['Grows up']
							end,
							type = 'toggle',
							get = "HasReverseGrowth",
							set = function(info, v)
								info.handler:ReverseGrowth(v)
								info.handler.db.reverseGrowth = v
							end
						},
						maxBars = {
							order = 140,
							name = L['Maximum bar number'],
							desc = L['Do not display more than this number of bars.'],
							type = 'range',
							min = 0,
							max = 20,
							step = 1,
							get = function(info) return info.handler:GetMaxBars() or 0 end,
							set = function(info, v)
								if v == 0 then
									v = nil
								end
								info.handler:SetMaxBars(v)
								info.handler.db.maxBars = v
							end
						},
					},
				},
				content = {
					order = 140,
					name = L['Content'],
					type = 'group',
					inline = true,
					args = {
						displayHeaders = {
							order = 175,
							name = L['Display headers'],
							desc = L['Display unit header bars'],
							type = 'toggle',
							get = function(info) return info.handler.db.displayHeaders end,
							set = function(info, v)
								info.handler.db.displayHeaders = v
								if v then
									Yatba:AddHeaders(info.handler)
								else
									Yatba:SendSignal('HeadersDisabled_'..info.handler.name)
								end
							end
						},
						template = {
							order = 180,
							name = L['Bar text template'],
							type = 'input',
							usage = '<text template with the following macros : $n for target Name, $s for Spell name, $a for number of Applications>',
							get = function(info) return info.handler.db.textTemplate end,
							set = function(info, v)
								v = v:trim()
								info.handler.db.textTemplate = v
								Yatba:SendSignal('TextTemplateUpdated', info.handler, v)
							end,
						},
						fill = {
							order = 120,
							name = L['Fill bars'],
							desc = L['Fill the bars instead of depleting them'],
							type = 'toggle',
							get = 'IsFilling',
							set = function(info, v)
								info.handler:SetFill(v)
								info.handler.db.fill = v
							end
						},
					},
				},
			},
		},
	}
}

local groupOptionsMetaTable = { __index = groupOptions }

function Yatba:AddGroupOption(group, name)
	local optionName = 'Group-'..name
	if not globalGroupOptionList[optionName] then
		globalGroupOptionList[optionName] = setmetatable(new(), groupOptionsMetaTable)
		globalGroupOptionList[optionName].handler = group
	end
end

function Yatba:RemoveGroupOption(name, group)
	local optionName = 'Group-'..group.name
	if globalGroupOptionList[optionName] then
		globalGroupOptionList[optionName] = del(globalGroupOptionList[optionName])
	end
end

--------------------------------------------------------------------------------
-- Aura options
--------------------------------------------------------------------------------

local auraOptionPrototype = {
	get = function(info)
		return db.auraRules[info.arg]
	end,
	set = function(info, value)
		db.auraRules[info.arg] = value
		Yatba:SendSignal('AuraFilterUpdated')
	end,
	type = 'select',
	values = {
		default = L['Apply default rule'],
		ignore  = L['Hide'],
		mine    = L['Show mine'],
		group   = L['Specific bar-group rule'],
		all     = L["Show anyone's"],
	},
}

local groupAuraOptionPrototype = {
	type = 'select',
	values = {
		ignore  = L['Hide'],
		mine    = L['Show mine'],
		all     = L["Show anyone's"]
	},
	get = function(info)
		return info.handler.db.auraRules[info.arg]
	end,
	set = function(info, enable)
		info.handler.db.auraRules[info.arg] = enable
		Yatba:UpdateFilter(info.handler)
	end,
	hidden = function(info)
		return not db.advancedFiltering or db.auraRules[info.arg] ~= 'group'
	end
}

local auraOptionMetatable = { __index = auraOptionPrototype }
local groupAuraOptionMetatable = { __index = groupAuraOptionPrototype }

function Yatba:AddAuraOptions(auraId, auraName)
	local optName = tostring(auraId)
	if not globalAuraOptionList[optName] then
		local option = setmetatable(new(), auraOptionMetatable)
		option.arg = auraId
		option.name = auraName
		globalAuraOptionList[optName] = option
	end
	if not groupAuraOptionList[optName] then
		local option = setmetatable(new(), groupAuraOptionMetatable)
		option.arg = auraId
		option.name = auraName
		groupAuraOptionList[optName] = option
	end
end

function Yatba:RemoveAuraOptions(auraId)
	local optName = tostring(auraId)
	globalAuraOptionList[optName] = del(globalAuraOptionList[optName])
	groupAuraOptionList[optName] = del(groupAuraOptionList[optName])
end

function Yatba:ResetAuraOptions()
	for key,value in pairs(globalAuraOptionList) do
		globalAuraOptionList[key] = del(value)
	end
	for key,value in pairs(groupAuraOptionList) do
		groupAuraOptionList[key] = del(value)
	end
end

